{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Transformers installation\n",
    "! pip install transformers datasets evaluate accelerate\n",
    "# To install from source instead of the last release, comment the command above and uncomment the following one.\n",
    "# ! pip install git+https://github.com/huggingface/transformers.git"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# الاختيار من متعدد (Multiple choice)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "مهمة الاختيار من متعدد مشابهة لمهمة الإجابة على الأسئلة، ولكن مع توفير عدة إجابات محتملة مع سياق، ويُدرّب النموذج على تحديد الإجابة الصحيحة.\n",
    "\n",
    "سيوضح لك هذا الدليل كيفية:\n",
    "\n",
    "1. ضبط نموذج [BERT](https://huggingface.co/google-bert/bert-base-uncased)  باستخدام الإعداد `regular` لمجموعة بيانات [SWAG](https://huggingface.co/datasets/swag) لاختيار الإجابة الأفضل من بين الخيارات المتعددة المتاحة مع السياق.\n",
    "2. استخدام النموذج المضبوط للاستدلال.\n",
    "\n",
    "قبل البدء، تأكد من تثبيت جميع المكتبات الضرورية:\n",
    "\n",
    "```bash\n",
    "pip install transformers datasets evaluate\n",
    "```\n",
    "\n",
    "نشجعك على تسجيل الدخول إلى حساب Hugging Face الخاص بك حتى تتمكن من تحميل نموذجك ومشاركته مع المجتمع. عند المطالبة، أدخل الرمز المميز الخاص بك لتسجيل الدخول:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from huggingface_hub import notebook_login\n",
    "\n",
    "notebook_login()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## تحميل مجموعة بيانات SWAG"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "ابدأ بتحميل تهيئة `regular` لمجموعة بيانات SWAG من مكتبة 🤗 Datasets:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from datasets import load_dataset\n",
    "\n",
    "swag = load_dataset(\"swag\", \"regular\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "ثم ألق نظرة على مثال:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'ending0': 'passes by walking down the street playing their instruments.',\n",
       " 'ending1': 'has heard approaching them.',\n",
       " 'ending2': \"arrives and they're outside dancing and asleep.\",\n",
       " 'ending3': 'turns the lead singer watches the performance.',\n",
       " 'fold-ind': '3416',\n",
       " 'gold-source': 'gold',\n",
       " 'label': 0,\n",
       " 'sent1': 'Members of the procession walk down the street holding small horn brass instruments.',\n",
       " 'sent2': 'A drum line',\n",
       " 'startphrase': 'Members of the procession walk down the street holding small horn brass instruments. A drum line',\n",
       " 'video-id': 'anetv_jkn6uvmqwh4'}"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "swag[\"train\"][0]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "على الرغم من أن الحقول تبدو كثيرة، إلا أنها في الواقع بسيطة جداً:\n",
    "\n",
    "- `sent1` و `sent2`: يعرض هذان الحقلان بداية الجملة، وبدمجهما معًا، نحصل على حقل `startphrase`.\n",
    "- `ending`: يقترح نهاية محتملة للجملة، واحدة منها فقط هي الصحيحة.\n",
    "- `label`: يحدد نهاية الجملة الصحيحة."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## المعالجة المسبقة (Preprocess)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "الخطوة التالية هي استدعاء مُجزئ BERT لمعالجة بدايات الجمل والنهايات الأربع المحتملة:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from transformers import AutoTokenizer\n",
    "\n",
    "tokenizer = AutoTokenizer.from_pretrained(\"google-bert/bert-base-uncased\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "تحتاج دالة المعالجة المسبقة التي تريد إنشاءها إلى:\n",
    "\n",
    "1.  إنشاء أربع نسخ من حقل `sent1` ودمج كل منها مع `sent2` لإعادة إنشاء كيفية بدء الجملة.\n",
    "2. دمج `sent2` مع كل من نهايات الجمل الأربع المحتملة.\n",
    "3. تتجميع هاتين القائمتين لتتمكن من تجزئتهما، ثم إعادة ترتيبها بعد ذلك بحيث يكون لكل مثال حقول `input_ids` و `attention_mask` و `labels` مقابلة."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ending_names = [\"ending0\", \"ending1\", \"ending2\", \"ending3\"]\n",
    "\n",
    "def preprocess_function(examples):\n",
    "    first_sentences = [[context] * 4 for context in examples[\"sent1\"]]\n",
    "    question_headers = examples[\"sent2\"]\n",
    "    second_sentences = [\n",
    "        [f\"{header} {examples[end][i]}\" for end in ending_names] for i, header in enumerate(question_headers)\n",
    "    ]\n",
    "\n",
    "    first_sentences = sum(first_sentences, [])\n",
    "    second_sentences = sum(second_sentences, [])\n",
    "\n",
    "    tokenized_examples = tokenizer(first_sentences, second_sentences, truncation=True)\n",
    "    return {k: [v[i : i + 4] for i in range(0, len(v), 4)] for k, v in tokenized_examples.items()}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "لتطبيق دالة المعالجة المسبقة على مجموعة البيانات بأكملها، استخدم طريقة `map` الخاصة بـ 🤗 Datasets. يمكنك تسريع دالة `map` عن طريق تعيين `batched=True` لمعالجة عناصر متعددة من مجموعة البيانات في وقت واحد:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tokenized_swag = swag.map(preprocess_function, batched=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "لا يحتوي 🤗 Transformers على مجمع بيانات للاختيار من متعدد، لذلك ستحتاج إلى تكييف `DataCollatorWithPadding` لإنشاء دفعة من الأمثلة. من الأكفأ إضافة حشو (padding) ديناميكي للجمل إلى أطول طول في دفعة أثناء التجميع، بدلاً من حشو مجموعة البيانات بأكملها إلى الحد الأقصى للطول.\n",
    "\n",
    "يقوم `DataCollatorForMultipleChoice` بتجميع جميع مدخلات النموذج، ويطبق الحشو، ثم يعيد تجميع النتائج في شكلها الأصلي:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from dataclasses import dataclass\n",
    "from transformers.tokenization_utils_base import PreTrainedTokenizerBase, PaddingStrategy\n",
    "from typing import Optional, Union\n",
    "import torch\n",
    "\n",
    "@dataclass\n",
    "class DataCollatorForMultipleChoice:\n",
    "    \"\"\"\n",
    "    Data collator that will dynamically pad the inputs for multiple choice received.\n",
    "    \"\"\"\n",
    "\n",
    "    tokenizer: PreTrainedTokenizerBase\n",
    "    padding: Union[bool, str, PaddingStrategy] = True\n",
    "    max_length: Optional[int] = None\n",
    "    pad_to_multiple_of: Optional[int] = None\n",
    "\n",
    "    def __call__(self, features):\n",
    "        label_name = \"label\" if \"label\" in features[0].keys() else \"labels\"\n",
    "        labels = [feature.pop(label_name) for feature in features]\n",
    "        batch_size = len(features)\n",
    "        num_choices = len(features[0][\"input_ids\"])\n",
    "        flattened_features = [\n",
    "            [{k: v[i] for k, v in feature.items()} for i in range(num_choices)] for feature in features\n",
    "        ]\n",
    "        flattened_features = sum(flattened_features, [])\n",
    "\n",
    "        batch = self.tokenizer.pad(\n",
    "            flattened_features,\n",
    "            padding=self.padding,\n",
    "            max_length=self.max_length,\n",
    "            pad_to_multiple_of=self.pad_to_multiple_of,\n",
    "            return_tensors=\"pt\",\n",
    "        )\n",
    "\n",
    "        batch = {k: v.view(batch_size, num_choices, -1) for k, v in batch.items()}\n",
    "        batch[\"labels\"] = torch.tensor(labels, dtype=torch.int64)\n",
    "        return batch"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## التقييم (Evaluate)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "يُفضل غالبًا تضمين مقياس أثناء التدريب لتقييم أداء نموذجك. يمكنك تحميل طريقة تقييم بسرعة باستخدام مكتبة 🤗 [Evaluate](https://huggingface.co/docs/evaluate/index). لهذه المهمة، قم بتحميل مقياس [الدقة](https://huggingface.co/spaces/evaluate-metric/accuracy) (انظر إلى [الجولة السريعة](https://huggingface.co/docs/evaluate/a_quick_tour) لـ 🤗 Evaluate لمعرفة المزيد حول كيفية تحميل المقياس وحسابه):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import evaluate\n",
    "\n",
    "accuracy = evaluate.load(\"accuracy\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "ثم أنشئ دالة لتمرير التنبؤات والتسميات إلى `compute` لحساب الدقة:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "def compute_metrics(eval_pred):\n",
    "    predictions, labels = eval_pred\n",
    "    predictions = np.argmax(predictions, axis=1)\n",
    "    return accuracy.compute(predictions=predictions, references=labels)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "دالتك `compute_metrics` جاهزة الآن، وستعود إليها عند إعداد تدريبك."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## التدريب (Train)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<Tip>\n",
    "\n",
    "إذا لم تكن معتادًا على ضبط نموذج باستخدام `Trainer`, فراجع الدرس الأساسي [هنا](https://huggingface.co/docs/transformers/main/ar/tasks/../training#train-with-pytorch-trainer)!\n",
    "\n",
    "</Tip>\n",
    "\n",
    "أنت جاهز لبدء تدريب نموذجك الآن! قم بتحميل BERT باستخدام `AutoModelForMultipleChoice`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from transformers import AutoModelForMultipleChoice, TrainingArguments, Trainer\n",
    "\n",
    "model = AutoModelForMultipleChoice.from_pretrained(\"google-bert/bert-base-uncased\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "في هذه المرحلة، تبقى ثلاث خطوات فقط:\n",
    "\n",
    "1. حدد معلمات التدريب الخاصة بك في `TrainingArguments`. المعلمة الوحيدة المطلوبة هي `output_dir` التي تحدد مكان حفظ نموذجك. ستدفع هذا النموذج إلى Hub عن طريق تعيين `push_to_hub=True` (يجب عليك تسجيل الدخول إلى Hugging Face لتحميل نموذجك). في نهاية كل حقبة، سيقوم `Trainer` بتقييم الدقة وحفظ نقطة فحص التدريب.\n",
    "2. مرر معلمات التدريب إلى `Trainer` جنبًا إلى جنب مع النموذج ومُجمِّع البيانات والمعالج ودالة تجميع البيانات ودالة `compute_metrics`.\n",
    "3. استدعي `train()` لضبط نموذجك."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "training_args = TrainingArguments(\n",
    "    output_dir=\"my_awesome_swag_model\",\n",
    "    eval_strategy=\"epoch\",\n",
    "    save_strategy=\"epoch\",\n",
    "    load_best_model_at_end=True,\n",
    "    learning_rate=5e-5,\n",
    "    per_device_train_batch_size=16,\n",
    "    per_device_eval_batch_size=16,\n",
    "    num_train_epochs=3,\n",
    "    weight_decay=0.01,\n",
    "    push_to_hub=True,\n",
    ")\n",
    "\n",
    "trainer = Trainer(\n",
    "    model=model,\n",
    "    args=training_args,\n",
    "    train_dataset=tokenized_swag[\"train\"],\n",
    "    eval_dataset=tokenized_swag[\"validation\"],\n",
    "    processing_class=tokenizer,\n",
    "    data_collator=DataCollatorForMultipleChoice(tokenizer=tokenizer),\n",
    "    compute_metrics=compute_metrics,\n",
    ")\n",
    "\n",
    "trainer.train()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "بمجرد اكتمال التدريب، شارك نموذجك مع Hub باستخدام طريقة `push_to_hub()` حتى يتمكن الجميع من استخدام نموذجك:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "trainer.push_to_hub()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<Tip>\n",
    "\n",
    "للحصول على مثال أكثر تعمقًا حول كيفية ضبط نموذج للاختيار من متعدد، ألق نظرة على [دفتر ملاحظات PyTorch](https://colab.research.google.com/github/huggingface/notebooks/blob/main/examples/multiple_choice.ipynb)\n",
    "أو [دفتر ملاحظات TensorFlow](https://colab.research.google.com/github/huggingface/notebooks/blob/main/examples/multiple_choice-tf.ipynb) المقابل.\n",
    "\n",
    "</Tip>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## الاستدلال  (Inference)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "رائع، الآن بعد أن قمت بضبط نموذج، يمكنك استخدامه للاستدلال!\n",
    "\n",
    "قم بإنشاء نص واقتراح إجابتين محتملتين:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "prompt = \"France has a bread law, Le Décret Pain, with strict rules on what is allowed in a traditional baguette.\"\n",
    "candidate1 = \"The law does not apply to croissants and brioche.\"\n",
    "candidate2 = \"The law applies to baguettes.\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "قم بتحليل كل مطالبة وزوج إجابة مرشح وأعد تنسورات PyTorch. يجب عليك أيضًا إنشاء بعض `العلامات`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from transformers import AutoTokenizer\n",
    "\n",
    "tokenizer = AutoTokenizer.from_pretrained(\"username/my_awesome_swag_model\")\n",
    "inputs = tokenizer([[prompt, candidate1], [prompt, candidate2]], return_tensors=\"pt\", padding=True)\n",
    "labels = torch.tensor(0).unsqueeze(0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "مرر مدخلاتك والعلامات إلى النموذج وأرجع`logits`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from transformers import AutoModelForMultipleChoice\n",
    "\n",
    "model = AutoModelForMultipleChoice.from_pretrained(\"username/my_awesome_swag_model\")\n",
    "outputs = model(**{k: v.unsqueeze(0) for k, v in inputs.items()}, labels=labels)\n",
    "logits = outputs.logits"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "استخرج الفئة ذات الاحتمالية الأكبر:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "predicted_class = logits.argmax().item()\n",
    "predicted_class"
   ]
  }
 ],
 "metadata": {},
 "nbformat": 4,
 "nbformat_minor": 4
}
